use bytes::BytesMut;

use crate::{
    extract_fixed_data, parse_length, resp::BUFFER_CAP, RespDecode, RespEncode, RespError,
    RespFrame,
};
use std::ops::Deref;

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RespNullArray;

#[derive(Debug, PartialEq, Clone)]
pub struct RespArray(pub Vec<RespFrame>);

impl Deref for RespArray {
    type Target = Vec<RespFrame>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl RespArray {
    pub fn new(s: impl Into<Vec<RespFrame>>) -> Self {
        RespArray(s.into())
    }
}

// *<number-of-elements>\r\n<element-1>...<element-n>
impl RespEncode for RespArray {
    fn encode(self) -> Vec<u8> {
        let mut buf = Vec::with_capacity(BUFFER_CAP);
        buf.extend_from_slice(&format!("*{}\r\n", self.len()).into_bytes());
        for frame in self.0 {
            buf.extend_from_slice(&frame.encode());
        }
        buf
    }
}

// *-1\r\n
impl RespEncode for RespNullArray {
    fn encode(self) -> Vec<u8> {
        b"*-1\r\n".to_vec()
    }
}

// *<number-of-elements>\r\n<element-1>...<element-n>
impl RespDecode for RespArray {
    fn decode(buf: &mut BytesMut) -> Result<Self, RespError> {
        let len = parse_length(buf, "*")?;

        let mut frames = Vec::with_capacity(len);
        for _ in 0..len {
            frames.push(RespFrame::decode(buf)?);
        }

        Ok(RespArray::new(frames))
    }
}

// *-1\r\n
impl RespDecode for RespNullArray {
    fn decode(buf: &mut BytesMut) -> Result<Self, RespError> {
        match extract_fixed_data(buf, "*-1\r\n", "NullArray") {
            Ok(_) => Ok(RespNullArray),
            Err(e) => Err(e),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{BulkString, RespFrame, SimpleString};

    #[test]
    fn array_encode_test() {
        let frame: RespFrame = RespArray::new(vec![
            SimpleString::new("get".to_string()).into(),
            SimpleString::new("hello".to_string()).into(),
            BulkString::new(b"world".to_vec()).into(),
        ])
        .into();
        assert_eq!(frame.encode(), b"*3\r\n+get\r\n+hello\r\n$5\r\nworld\r\n");
    }

    #[test]
    fn null_array_encode_test() {
        let frame: RespFrame = RespNullArray.into();
        assert_eq!(frame.encode(), b"*-1\r\n");
    }

    #[test]
    fn array_decode_test() -> anyhow::Result<()> {
        let mut buf = BytesMut::new();
        buf.extend_from_slice(b"*2\r\n$3\r\nset\r\n$5\r\nhello\r\n");
        let frame = RespArray::decode(&mut buf)?;

        eprintln!("{:?}", frame);
        assert_eq!(
            frame,
            RespArray::new(vec![
                BulkString::new(b"set".to_vec()).into(),
                BulkString::new(b"hello".to_vec()).into()
            ])
        );
        Ok(())
    }
}
